import java.util.Calendar;
import java.util.LinkedList;

/*
 * This class simulates a resource, which is a set of servers. Each resource object has a unique id and a resource 
 * size which represents the proportion of servers it contain. So depending on the resource and the type of incoming 
 * job, the resource's power consumption would increase or decrease.
 */

public class Resource implements IResource 
{
	//private int resource_id;
	private int resource_proportion;
	private int remainingResourceProportion;
	private LinkedList<IJob> current_unserviced_jobs = null;
	private LinkedList<IServicedJob> current_serviced_jobs = null;
	private Double powerConsumption;
	private Double power_factor; // input the resource factor from the constructor function
	
	protected volatile Boolean running = null;
	
	public Resource(int r_proportion, Double pF,Boolean running)
	{
		//this.resource_id = r_id;
		this.resource_proportion = r_proportion;
		this.remainingResourceProportion = this.resource_proportion;
		this.power_factor = pF;
		this.running = running;
		powerConsumption = 1.0;
		
		current_unserviced_jobs = new LinkedList<IJob>();
		current_serviced_jobs = new LinkedList<IServicedJob>();
		
		startPowerComputationThread();
	}
	
	private void startPowerComputationThread()
	{
		Runnable CalculatePowerConsumption = new Runnable()
		{
			public void run()
			{
				/*
				 *  This thread would calculate the current power consumption of it's resource in real time.
				 *  It takes its decision based on the following:
				 *  
				 *  			1. The amount of jobs present in the job queue
				 *  			2. The characteristics of each job i.e
				 *  					a. The duration
				 *  					b. The resource requirement
				 *  			3. The resource_proportion free in the resource for use by the job
				 */
				
				while(running)
				{
					if(current_unserviced_jobs.isEmpty())
						powerConsumption = 1.0;
					
					else
					{
						/*
						 *  Do the required calculations as done in the paper.
						 *  For the entire list of jobs present:
						 *  	1. Calculate the amount of free resource proportion. 
						 *  	2. Then according to that value and the characteristics of the given job, calculate 
						 *  		the power consumption to be added.
						 *  	3. Also when a job finishes, deduct the power consumption value for the resource's
						 *  		current consumption. 
						 */
						
						// loop for checking termination of serviced job and calculate DECREASE in power consumption.
						for(int j=0;j<current_serviced_jobs.size();j++)
						{
							// iterate through each serviced job and check for termination
							// if terminated , then reduce the power consumed and increase the resource proportion accordingly.
						
							if(current_serviced_jobs.get(j).isJobOver(Calendar.getInstance()))
							{
								// do as stated
								
								powerConsumption = powerConsumption - current_serviced_jobs.get(j).getPowerConsumed();
								remainingResourceProportion = remainingResourceProportion + current_serviced_jobs.get(j).getJob().getResourceRequirementLevel();
								
								// remove the job from the list
								
								current_serviced_jobs.remove(j);
							}
							
							else
							{
								// do nothing
							}
						}
						
						// loop for servicing jobs and calculating INCREASE in power consumption.
						for(int i=0;i<current_unserviced_jobs.size();i++)
						{
							// iterate through each job and do as stated above
							
							if(remainingResourceProportion >= current_unserviced_jobs.get(i).getResourceRequirementLevel())
							{
								// Calculate power consumption for the request.
								// Decrement the remaining resource proportion.
								
								remainingResourceProportion = remainingResourceProportion - current_unserviced_jobs.get(i).getResourceRequirementLevel();
								
								Double extraConsumption = new Double (current_unserviced_jobs.get(i).getResourceRequirementLevel()
																		* current_unserviced_jobs.get(i).getDuration()
																		* power_factor);
								
								powerConsumption = powerConsumption + extraConsumption;
								
								// Remove the request from the unserviced list and put it in serviced list.
								
								IServicedJob newServicedJob = new ServicedJob(current_unserviced_jobs.remove(i),Calendar.getInstance(),extraConsumption);
								current_serviced_jobs.add(newServicedJob);
							}
							
							else
							{
								// wait for part of the resource to be free and then process it.
								System.out.println("No resource free currently for job: " + current_unserviced_jobs.get(i));
							}
						}
					}
					
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						System.out.println("Exception in power computation resource thread sleeping.");
						e.printStackTrace();
					}
				}
			}
		};
		
		Thread powerConsumptionThread = new Thread(CalculatePowerConsumption);
		powerConsumptionThread.start();
	}
	
	public void assignJob(IJob new_job)
	{
		this.current_unserviced_jobs.add(new_job);
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			System.out.println("Exception in resource object sleeping");
			e.printStackTrace();
		}
		
		//calculatePowerConsumption();
	}
	
	/*private void calculatePowerConsumption()
	{
		// calculate probable power consumption according to the given job, and the type of resource i.e resource size
	
		for(int i=0;i<current_jobs.size();i++)
		{
			powerConsumption = (powerConsumption + (new Double((current_jobs.get(i).getResourceRequirementLevel() * current_jobs.get(i).getDuration())/resource_proportion)))/2;
			current_jobs.remove(i);
		}
	}*/
	
	public double getPowerConsumption()
	{
		return powerConsumption.doubleValue();
	}
	
	/*public int getResourceID()
	{
		return resource_id;
	}*/
}
